/**
 * Copyright (C) 2021 - 2031 O-Cubes Co., Ltd.
 */

/****************************************************************
 *  @file    syscounter.c
 *  @brief   SYSCOUNTER driver
 *  @version v1.0
 *  @date    03. Apr. 2023
 *  @author  gonght
 ****************************************************************/

#include "mem_map_table.h"
//#include "autoconf.h"
#include "io.h"
#include "syscounter.h"
#include "crg.h"

#define SYSCNT_REG_BASE                   MEM_MAP_SYSCNT_BASE_ADDR
#define SYSCNT_REG_CTRL                   SYSCNT_REG_BASE
#define SYSCNT_REG_STATUS                 (SYSCNT_REG_BASE + 0x4)
#define SYSCNT_REG_CURR_VALUE_L           (SYSCNT_REG_BASE + 0x8)
#define SYSCNT_REG_CURR_VALUE_H           (SYSCNT_REG_BASE + 0xC)

/* Base frequency ID */
#define SYSCNT_REG_FREQUENCY_ID           (SYSCNT_REG_BASE + 0x20)

/* Peripheral ID */
#define SYSCNT_REG_PERIPHERAL_ID_4        (SYSCNT_REG_BASE + 0xFD0)
#define SYSCNT_REG_PERIPHERAL_ID_5        (SYSCNT_REG_BASE + 0xFD4)
#define SYSCNT_REG_PERIPHERAL_ID_6        (SYSCNT_REG_BASE + 0xFD8)
#define SYSCNT_REG_PERIPHERAL_ID_7        (SYSCNT_REG_BASE + 0xFDC)
#define SYSCNT_REG_PERIPHERAL_ID_0        (SYSCNT_REG_BASE + 0xFE0)
#define SYSCNT_REG_PERIPHERAL_ID_1        (SYSCNT_REG_BASE + 0xFE4)
#define SYSCNT_REG_PERIPHERAL_ID_2        (SYSCNT_REG_BASE + 0xFE8)
#define SYSCNT_REG_PERIPHERAL_ID_3        (SYSCNT_REG_BASE + 0xFEC)

/* Compare ID */
#define SYSCNT_REG_COMP_ID_0              (SYSCNT_REG_BASE + 0xFF0)
#define SYSCNT_REG_COMP_ID_1              (SYSCNT_REG_BASE + 0xFF4)
#define SYSCNT_REG_COMP_ID_2              (SYSCNT_REG_BASE + 0xFF8)
#define SYSCNT_REG_COMP_ID_3              (SYSCNT_REG_BASE + 0xFFC)

static void syscnt_init_oper(void)
{
	/* write peripheral ID registers */
	writel(0xC0300F00, SYSCNT_REG_PERIPHERAL_ID_0);
	writel(0xC0300F01, SYSCNT_REG_PERIPHERAL_ID_1);
	writel(0xC0300F02, SYSCNT_REG_PERIPHERAL_ID_2);
	writel(0xC0300F03, SYSCNT_REG_PERIPHERAL_ID_3);
	writel(0xC0300F04, SYSCNT_REG_PERIPHERAL_ID_4);
	writel(0xC0300F05, SYSCNT_REG_PERIPHERAL_ID_5);
	writel(0xC0300F06, SYSCNT_REG_PERIPHERAL_ID_6);
	writel(0xC0300F07, SYSCNT_REG_PERIPHERAL_ID_7);

	/* write compare ID registers */
	writel(0xC0300FA0, SYSCNT_REG_COMP_ID_0);
	writel(0xC0300FA1, SYSCNT_REG_COMP_ID_1);
	writel(0xC0300FA2, SYSCNT_REG_COMP_ID_2);
	writel(0xC0300FA3, SYSCNT_REG_COMP_ID_3);
}

static void syscnt_enable(syscnt_ctrl en)
{
	writel(en, SYSCNT_REG_CTRL);
}

u64 syscnt_read_counter(void)
{
	u32 low = 0;
	u64 high = 0;
	u64 val = 0;

	low = readl(SYSCNT_REG_CURR_VALUE_L);
	high = readl(SYSCNT_REG_CURR_VALUE_H);
	val = (high << 32);
	val |= low;

	return val;
}

void syscnt_set_counter(u32 high, u32 low)
{
	syscnt_enable(SYSCNT_DISABLE);

	writel(low, SYSCNT_REG_CURR_VALUE_L);
	writel(high, SYSCNT_REG_CURR_VALUE_H);
}

void syscnt_init(void)
{
	syscnt_init_oper();
	syscnt_set_counter(0, 0);
	syscnt_enable(SYSCNT_ENABLE);
}

static u64 syscnt_usec_to_tick(u32 usec)
{
	u64 tick = usec;

	tick *= get_mod_clk_rate(CRG_MOD_SYSCNT);
	tick /= US_PER_SECOND;

	return tick;
}

u64 syscnt_tick_to_usec(u64 tick)
{
	u64 usec = 0;

	usec = (tick * US_PER_SECOND) / get_mod_clk_rate(CRG_MOD_SYSCNT);
	return usec;
}

void syscnt_udelay(u32 usec)
{
	u64 initval = 0;
	u64 currval = 0;
	u64 timeout = 0;

	initval = syscnt_read_counter();

	timeout = initval + syscnt_usec_to_tick(usec);

	do {
		currval = syscnt_read_counter();
	} while (currval < timeout + 1);
}

void syscnt_mdelay(u32 msec)
{
	u32 msval = msec * US_PER_MS;

	syscnt_udelay(msval);
}

void syscnt_delay(u32 sec)
{
	u32 sval = sec * US_PER_SECOND;

	syscnt_udelay(sval);
}

